#include "tableManager.h"
#include <memory>



class multiSave :public QRunnable{
    const string tmp;
    const string data;
    const vector<vector<int>> len_data;
    static int count;
public:
    multiSave(const string& tmp,const string& data,const vector<vector<int>>& len_data):tmp(tmp),data(data),len_data(len_data){}

    static void join(const int& target){
        while(true){
            if(count==target){
                IO::mutex.lock();
                count=0;
                IO::mutex.unlock();
                return;
            }
        }
    }

    virtual void run(){
        IO::write_to_file(tmp,data);
        IO::write_to_len_file(tmp,len_data);
        IO::mutex.lock();
        ++count;
        IO::mutex.unlock();
    }
};



class multiRead:public QRunnable{
    const string path;
    const int num;
    static int count;
public:
    static vector<string> allData;
    multiRead(const string& path,const int& num):path(path),num(num){}

    static void join(const int& target){
        while(true){
            if(count==target){
                IO::mutex.lock();
                count=0;
                IO::mutex.unlock();
                return;
            }
        }
    }

    virtual void run(){
        const string& data = IO::read_single_diskblock(path);
        IO::mutex.lock();
        ++count;
        multiRead::allData[num-1]=data;
        IO::mutex.unlock();
    }
};



class multiToStr:public QRunnable{
    const int num;
    const int fileLen;
    const string title;
    table* tmpTable;
    static int count;
public:
    static vector<string> dataVec;
    multiToStr(const int& num,const int& fileLen,const string& title,table* tmpTable):num(num),fileLen(fileLen),title(title),tmpTable(tmpTable){}

    static void join(const int& target){
        while(true){
            if(count==target){
                IO::mutex.lock();
                count=0;
                IO::mutex.unlock();
                return;
            }
        }
    }

    virtual void run(){
        string data=title;
        int start=num*fileLen;
        int m=min(tmpTable->getColLen(),start+fileLen);
        int n=tmpTable->getColNum();
        for(int i=start;i<m;++i){
            for(int j=0;j<n;++j){
                string tmpData=tmpTable->getLocStr(i,j);
                #ifdef CommaReplace
                tmpData=helper::toEscapeStr(tmpData);
                #endif
                if(j==n-1){
                    data.append(tmpData+"\n");
                }
                else{
                    data.append(tmpData+",");
                }
            }
        }
        IO::mutex.lock();
        ++count;
        dataVec[num]=data;
        IO::mutex.unlock();
    }
};

vector<string> multiRead::allData;
vector<string> multiToStr::dataVec;
int multiSave::count;
int multiRead::count;
int multiToStr::count;
QMutex IO::mutex;
map<string,int> IO::tablesBlockNum;

string table::getStrTitle(){
    string title="";
    int m=this->getCol(0)->getLen();
    int n=this->allCol.size();
    for(int i=0;i<n;++i){
        col* tmpCol=allCol[i];
        int dataType=this->allCol[i]->getType();
        int indexType=this->allIndex[i]->getIndexType();
        title.append(tmpCol->ID+":"+to_string(dataType)+":"+to_string(indexType));
        if(i==n-1){
            title.append("\n");
            continue;
        }
        title.append(",");
    }
    return title;
}

QString table::toStr()
{
    string data=this->getStrTitle();
    int m=this->getCol(0)->getLen();
    int n=this->allCol.size();
    for(int i=0;i<m;++i){
        for(int j=0;j<n;++j){
            const string& tmpData=this->getLocStr(i,j);
            if(j==n-1){
                data.append(tmpData+"\n");
            }
            else{
                data.append(tmpData+",");
            }
        }
    }
    return QString::fromStdString(data);
}


vector<string> table::toStr(const int& fileLen){
    int m=this->getCol(0)->getLen();
    const string& title=this->getStrTitle();
    int totalNum=(m/fileLen)+(m%fileLen==0? 0:1);
    if(totalNum==0){
        return {title};
    }
    multiToStr::dataVec=vector<string>(totalNum,"");
    for(int i=0;i<totalNum;++i){
        multiToStr* mt=new multiToStr (i,fileLen,title,this);
        mt->setAutoDelete(true);
        QThreadPool::globalInstance()->start(mt);
    }
    multiToStr::join(totalNum);
    return multiToStr::dataVec;
}

void table::loadStr(const int& num ,const string& path){
    multiRead::allData=vector<string>(num,"");
    for(int i=1;i<=num;++i){
        multiRead* mr=new multiRead (IO::path_to_splitpath(path,i),i);
        mr->setAutoDelete(true);
        QThreadPool::globalInstance()->start(mr);
    }
    multiRead::join(num);
}

void table:: saveFile(const string& ID) //将整个表的内容按约定格式写入空文件
{
    const string& path=IO::ID_to_path(ID);
    const vector<string>& allData=this->toStr(IO::singleFileLen);
    int num=1;
    for(const string& data:allData){
        const string& tmp=IO::path_to_splitpath(path,num);
        const vector<vector<int>>& len_data=IO::strdata_to_lendata(data);
        multiSave* ms= new multiSave(tmp,data,len_data);
        ms->setAutoDelete(true);
        QThreadPool::globalInstance()->start(ms);
        ++num;
    }
    multiSave::join(allData.size());
    IO::del_table_blocks(ID,num);
    IO::tablesBlockNum[ID]=allData.size();
    multiToStr::dataVec.clear();
    this->allRecord=list<record>();
}

table* table::loadFile(const string& ID) //按约定格式从文件中读取表
{
    const string& path=IO::ID_to_path(ID);
    vector<col*> cols;
    vector<IndexType> indTypes;
    vector<int> blocksLen;
    if(IO::tablesBlockNum.find(ID)==IO::tablesBlockNum.end()){
        IO::tablesBlockNum[ID]=IO::table_blocks_num(path);
    }
    int num=IO::tablesBlockNum[ID];
    if(num==0){
        throw string("No corresponding table was found");
    }
    loadStr(num,path);
    for(int i=0;i<multiRead::allData.size();++i){
        const string tmpData=multiRead::allData[i];
        if(i==0){
            const auto& tp=IO::get_empty_table_cols(tmpData);
            cols=get<0>(tp);
            indTypes=get<1>(tp);
        }
        const int& dataLen=IO::put_single_block_data(cols,tmpData);
        blocksLen.push_back(dataLen);
    }
    multiRead::allData.clear();
    return new table(ID,cols,blocksLen,true,indTypes);
}

void table::updateFile(const string& ID) //根据table.allRecord更新文件内容
{
    if(this->allRecord.empty()){
        return;
    }
    const string& path=IO::ID_to_path(ID);
    map<int,fstream> fileStore;
    map<int,vector<vector<int>>> lenStore;
    for(record rcd:this->allRecord){
        switch (rcd.type) {
        case ADD:{
            IO::Add(rcd.targetTuple,fileStore,lenStore,ID,this->allBlocksLen);
            break;
        }
        case DEL:{
            IO::Del(rcd.opSub,fileStore,lenStore,ID,this->allBlocksLen);
            break;
        }
        case MOD:{
            IO::Del(rcd.opSub,fileStore,lenStore,ID,this->allBlocksLen);
            IO::Add(rcd.targetTuple,fileStore,lenStore,ID,this->allBlocksLen);
            break;
        }
        }
    }
    for(auto iter=fileStore.begin();iter!=fileStore.end();iter++){
        iter->second.flush();
    }
    for(auto iter=lenStore.begin();iter!=lenStore.end();iter++){
        IO::write_to_len_file(IO::path_to_splitpath(path,iter->first),iter->second);
    }
    //全部写入后清除record记录
    this->allRecord=list<record>();
}

